#!/bin/bash

#
# Test an AppDir or AppImage on a given ISO or squashfs or ext3 base system
# TODO:
# - Make compatible with type 2 AppImages (currently we have to first mount them by hand)
# - Check https://github.com/FGrose/livecd-tools/blob/liveimage-mount/tools/liveimage-mount
#

set -e
# set -x

xhost + # Workaround for: QXcbConnection: Could not connect to display :0

HERE=$(dirname $(readlink -f "${0}"))
export PATH=$HERE:$HERE/usr/bin:$PATH

if [ "$1x" == "x" ] ; then
    echo "Please specify a ISO or squashfs base system to run the AppImage on"
    exit 1
fi

if [ "$2x" == "x" ] ; then
    echo "Please specify an AppDir or AppImage outside the ISO or command inside the ISO to be run"
    exit 1
fi

export PREF=$RANDOM

mkdir -p /tmp/$PREF/unionfs/ro
mkdir -p /tmp/$PREF/unionfs/root
mkdir -p /tmp/$PREF/unionfs/rw
mkdir -p /tmp/$PREF/union
mkdir -p /tmp/$PREF/iso
mkdir -p /tmp/$PREF/workdir

# openSUSE LEAP 42.2 does not have a tmpfs in /tmp but we need this
mount | grep -q "/tmp type tmpfs" || sudo mount -t tmpfs tmpfs /tmp/

# If ISO was specified, then mount it and find contained filesystem
THEFS="$1"
if [ ${1: -4} == ".iso" ] ; then
    ISO="$1"
    mount -o loop,ro "$ISO" /tmp/$PREF/iso
    # If there is a squashfs file named $ISO.buildsquash then we overlay-
    # mount this as well. This can significantly speed up subsequent builds
    # because dependencies can be installed there. The squashfs file can be
    # generated by squashing the unionfs/rw directory.
    if [ -e $ISO.buildsquash ] ; then
        mount -o loop,ro "$ISO.buildsquash" /tmp/$PREF/unionfs/ro
    fi
fi

# If sfs was provided, then assume it contains a rootfs use just that
THEFS="$1"
if [ ${1: -4} == ".sfs" ] ; then
    ISO="$1"
    mount -o loop,ro "$ISO" /tmp/$PREF/unionfs/root
fi

# If ext3 was provided, then assume it contains a rootfs use just that
THEFS="$1"
if [ ${1: -4} == "ext3" ] ; then
    ISO="$1"
    mount -o loop,ro "$ISO" /tmp/$PREF/unionfs/root
fi

echo ""
echo "===================================================="
echo ""
echo $ISO

# In case of Ubuntu-like ISOs
if [ -e /tmp/$PREF/iso/casper/filesystem.squashfs ] ; then
  THEFS=/tmp/$PREF/iso/casper/filesystem.squashfs
  mount "$THEFS" /tmp/$PREF/unionfs/root -o loop,ro || exit 1
fi

# In case of Fedora-like ISOs
if [ -e /tmp/$PREF/iso/LiveOS/squashfs.img ] ; then
    mount -o loop,ro /tmp/$PREF/iso/LiveOS/squashfs.img /tmp/$PREF/iso/
    if [ -e /tmp/$PREF/iso/LiveOS/ext3fs.img ] ; then
      # F23 and below use https://github.com/rhinstaller/livecd-tools
      THEFS=/tmp/$PREF/iso/LiveOS/ext3fs.img || exit 1
    fi
    if [ -e /tmp/$PREF/iso/LiveOS/rootfs.img ] ; then
      # F24 and above use https://github.com/rhinstaller/lorax and livemedia-creator
      THEFS=/tmp/$PREF/iso/LiveOS/rootfs.img || exit 1
    fi
    mount "$THEFS" /tmp/$PREF/unionfs/root -o loop,ro || exit 1
fi

# In case of debian-like ISOs
if [ -e /tmp/$PREF/iso/live/filesystem.squashfs ] ; then
  THEFS=/tmp/$PREF/iso/live/filesystem.squashfs
  mount "$THEFS" /tmp/$PREF/unionfs/root -o loop,ro || exit 1
fi

# In case of openSUSE-like ISOs
if [ -e /tmp/$PREF/iso/*-read-only.* ] ; then
  THEFS=$(ls /tmp/$PREF/iso/*-read-only.* | head -n 1)
  mount "$THEFS" /tmp/$PREF/unionfs/root -o loop,ro || exit 1
fi

# In case of puppy-like ISOs
if [ -e /tmp/$PREF/iso/puppy_*.sfs ] ; then
  THEFS=$(ls /tmp/$PREF/iso/puppy_*.sfs | head -n 1)
  mount "$THEFS" /tmp/$PREF/unionfs/root -o loop,ro || exit 1
fi

trap atexit EXIT

atexit()
{    set +e
    umount -l /tmp/$PREF/union/var/lib/dbus 2>/dev/null
    umount -l /tmp/$PREF/union/etc/resolv.conf 2>/dev/null
    umount -l /tmp/$PREF/union/proc 2>/dev/null
    umount -l /tmp/$PREF/union/dev 2>/dev/null
    umount -l /tmp/$PREF/union/mnt 2>/dev/null
    umount -l /tmp/$PREF/union 2>/dev/null
    umount -l /tmp/$PREF/unionfs/ro 2>/dev/null
    umount -l /tmp/$PREF/unionfs/root 2>/dev/null
    umount -l /tmp/$PREF/iso 2>/dev/null
    umount -l /tmp/$PREF/iso 2>/dev/null
    umount -l /tmp/$PREF/iso 2>/dev/null
    rm -r /tmp/$PREF/workdir
    rm -r /tmp/$PREF/unionfs/root
    rm -r /tmp/$PREF/unionfs/rw
    rm -r /tmp/$PREF/unionfs/ro
    rm -r /tmp/$PREF/unionfs
    rm -r /tmp/$PREF/union
    rm -r /tmp/$PREF/iso
    rm -r /tmp/$PREF/
}

# Kernel 4.0 or newer required
kernel_major=$(uname -r | cut -d . -f 1)
if [ ! $kernel_major -ge 4 ] ; then
    echo "Falling back to unionfs-fuse because kernel is not 4.x or newer"
    echo "This can potentially be unreliable"
    unionfs-fuse -o allow_other,use_ino,suid,dev,nonempty -ocow,chroot=/tmp/$PREF/unionfs/,max_files=32768 /rw=RW:/ro=RO:/root=RO /tmp/$PREF/union
else
    echo "Using OverlayFS because kernel is 4.x or newer"
    sudo mount -t overlay -o lowerdir=/tmp/$PREF/unionfs/ro:/tmp/$PREF/unionfs/root,upperdir=/tmp/$PREF/unionfs/rw,workdir=/tmp/$PREF/workdir none /tmp/$PREF/union
fi

ls /tmp/$PREF/union/mnt >/dev/null && MNT=/mnt
# ls /tmp/$PREF/union/automake >/dev/null && MNT=/automake || echo "" # Puppy

if [ "x$MNT" == "x" ] ; then
    echo "Could not find free mountpoint"
    exit 1
fi
if [ -f "$2" ] ; then
    mount "$2" /tmp/$PREF/union/$MNT -o loop,ro
elif [ -d "$2" ] ; then
    mount "$2" /tmp/$PREF/union/$MNT -o bind
else
    shift
    export RUN_COMMAND=$1
    export RUN_INSIDE=$@
fi

cat > /tmp/$PREF/union/run.sh <<EOF
#!/bin/sh
cat /etc/*release
echo ""
rm -rf /etc/pango
mkdir -p /etc/pango
pango-querymodules > '/etc/pango/pango.modules' 2>/dev/null # otherwise only squares instead of text
[ -f /si-chroot ] && ln -s /lib/ld-lsb.so.3 /lib/ld-linux.so.2
# LD_LIBRARY_PATH=$MNT/usr/lib:$MNT/usr/lib32:$MNT/usr/lib64:$MNT/lib/:$LD_LIBRARY_PATH ldd $MNT/usr/bin/*  $MNT/usr/lib/* 2>/dev/null | grep "not found" | sort | uniq
export HOME="/root"
export LANG="en_EN.UTF-8"
# export QT_PLUGIN_PATH=./lib/qt4/plugins ###################### !!!
#dbus-launch $MNT/AppRun || 
export LIBGL_DEBUG=verbose
export QT_DEBUG_PLUGINS=1
export LC_ALL=C.UTF-8
mkdir -p /usr/share/appimagekit
touch /usr/share/appimagekit/no_desktopintegration
dbus-uuidgen --ensure ; dbus-launch # Needed for CentOS 6.7 to launch apps that talk to dbus
if [ -f $MNT/usr/lib/qt5/plugins/platforms/libqxcb.so ] ; then
  export LD_LIBRARY_PATH=$MNT/usr/lib/:$LD_LIBRARY_PATH
  ##ldd $MNT/usr/lib/qt5/plugins/platforms/libqxcb.so | grep not
fi
##find $MNT/usr/lib -type f -exec sh -c 'ldd {} | grep "not found"'  2>/dev/null \;
if [ $(which $RUN_COMMAND) ] ; then
    exec $RUN_INSIDE
else
    $MNT/AppRun && echo "+++++++++ SUCCESS $ISO $2 +++++++++"
fi
EOF
chmod a+x /tmp/$PREF/union/run.sh
mount -t proc proc /tmp/$PREF/union/proc
mkdir -p /tmp/$PREF/union/var/lib/dbus
mount --bind /dev /tmp/$PREF/union/dev
mount --bind /var/lib/dbus /tmp/$PREF/union/var/lib/dbus
touch /tmp/$PREF/union/etc/resolv.conf || echo ""
mount --bind /etc/resolv.conf /tmp/$PREF/union/etc/resolv.conf
xhost local: >/dev/null 2>&1 # otherwise "cannot open display: :0.0"
echo ""
chroot /tmp/$PREF/union/ /run.sh # $MNT/AppRun
echo ""
exit $?
